HW5: Correspondence analysis

Работа Ли-Мин Владиславы
if (!require("tidyverse")) install.packages("tidyverse")
if (!require("skimr")) install.packages("skimr")

library(tidyverse)
library(skimr)

infinitives <- read_tsv("https://raw.githubusercontent.com/olesar/2025dav4compling/refs/heads/main/data/PeriphrasticFutureMidRussian.txt", col_names = TRUE) %>%
  as.data.frame() %>%
    select(!ends_with("_Imp")) %>%
      select(!быти_Lform)
Rows: 23 Columns: 25
── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr  (1): INF
dbl (24): быти_Fut, быти_Lform, мочи_Aor, мочи_Imp, мочи_Lform, мочи_Pres, начати_Aor, начати_Fut, начати_Imp, начати_Lform, почати_Aor, почати_Fu...

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

Проверка датасета

Пользуясь столбцом INF, дайте имя каждому ряду таблицы сопряженности (с помощью функции row.names или column_to_rownames). Оставьте в таблице только целочисленные данные. Выведите первые 6 строк таблицы. Проверьте, что таблица не содержит missing data.

# превращаем столбец INF в индекс
infinitives <- column_to_rownames(infinitives, 'INF')

# проверяем, нет ли пропущенных данных
sum(is.na(infinitives))
[1] 0
top_n(infinitives, 6)
Selecting by хотѣти_Pres

Chisq test

Примените тест Хи-квадрат Пирсона к таблице сопряженности.

Постройте таблицу с ожидаемыми значениями для таблицы сопряженности.

Предложите анализ теста Хи-квадрат. Что можно сказать об ассоциации между конструкциями (они обозначены вспомогательными глаголами в той или иной временной форме) и инфинитивами? Какие проблемы вы видите в применении теста Хи-квадрат к этим данным?

# применяем тест Хи-квадрат Пирсона
infinitives_chisq <- chisq.test(infinitives)
G2;H2;Warningh in chisq.test(infinitives) :
  аппроксимация на основе хи-квадрат может быть неправильнойg
infinitives_chisq

    Pearson's Chi-squared test

data:  infinitives
X-squared = 4569.9, df = 396, p-value < 2.2e-16

Статистическая интерпретация p-value

P-value меньше 0.05, а значит, при 5%-ом уровне значимости мы имеем основания отвергнуть нулевую гипотезу о независимости употребления вспомогательных глаголов и инфинитивов.

Содержательная интерпретация

Выбор начинательного глагола зависит от следующего за ним инфинитива.

# проверяем пропущенные значения
sum(is.na(infinitives))
[1] 0
# Таблица с ожидаемыми значениями
infinitives_chisq$expected
           быти_Fut  мочи_Aor мочи_Lform мочи_Pres начати_Aor начати_Fut начати_Lform почати_Aor почати_Fut почати_Lform  стати_Aor стати_Fut
бити      30.536356 0.9366980  2.4354149 12.083405  36.531223  3.5594525    0.4683490  2.9037639  0.9366980    11.989735 0.37467921  9.460650
быти      15.756202 0.4833191  1.2566296  6.234816  18.849444  1.8366125    0.2416595  1.4982891  0.4833191     6.186484 0.19332763  4.881523
воевати    7.250642 0.2224123  0.5782720  2.869119   8.674080  0.8451668    0.1112062  0.6894782  0.2224123     2.846878 0.08896493  2.246364
глаголати 15.477331 0.4747648  1.2343884  6.124465  18.515825  1.8041061    0.2373824  1.4717707  0.4747648     6.076989 0.18990590  4.795124
говорити  47.826347 1.4670659  3.8143713 18.925150  57.215569  5.5748503    0.7335329  4.5479042  1.4670659    18.778443 0.58682635 14.817365
дати       8.226689 0.2523524  0.6561163  3.255346   9.841745  0.9589393    0.1261762  0.7822926  0.2523524     3.230111 0.10094098  2.548760
держати    9.202737 0.2822926  0.7339607  3.641574  11.009410  1.0727117    0.1411463  0.8751069  0.2822926     3.613345 0.11291702  2.851155
дѣлати    14.361848 0.4405475  1.1454234  5.683062  17.181352  1.6740804    0.2202737  1.3656972  0.4405475     5.639008 0.17621899  4.449530
жаловати   7.111206 0.2181352  0.5671514  2.813944   8.507271  0.8289136    0.1090676  0.6762190  0.2181352     2.792130 0.08725406  2.203165
жити      30.954662 0.9495295  2.4687767 12.248931  37.031651  3.6082121    0.4747648  2.9435415  0.9495295    12.153978 0.37981180  9.590248
имѣти      8.923867 0.2737382  0.7117194  3.531223  10.675791  1.0402053    0.1368691  0.8485885  0.2737382     3.503849 0.10949530  2.764756
искати    20.218135 0.6201882  1.6124893  8.000428  24.187340  2.3567151    0.3100941  1.9225834  0.6201882     7.938409 0.24807528  6.263901
итти      17.011121 0.5218135  1.3567151  6.731394  20.350727  1.9828914    0.2609068  1.6176219  0.5218135     6.679213 0.20872541  5.270317
писати    14.222412 0.4362703  1.1343028  5.627887  17.014542  1.6578272    0.2181352  1.3524380  0.4362703     5.584260 0.17450813  4.406330
послати    8.784431 0.2694611  0.7005988  3.476048  10.508982  1.0239521    0.1347305  0.8353293  0.2694611     3.449102 0.10778443  2.721557
посылати   7.947819 0.2437981  0.6338751  3.144996   9.508127  0.9264328    0.1218991  0.7557742  0.2437981     3.120616 0.09751925  2.462361
просити   10.457656 0.3207870  0.8340462  4.138152  12.510693  1.2189906    0.1603935  0.9944397  0.3207870     4.106074 0.12831480  3.239949
пѣти       8.784431 0.2694611  0.7005988  3.476048  10.508982  1.0239521    0.1347305  0.8353293  0.2694611     3.449102 0.10778443  2.721557
служити   10.597092 0.3250642  0.8451668  4.193328  12.677502  1.2352438    0.1625321  1.0076989  0.3250642     4.160821 0.13002566  3.283148
сотворити  8.505560 0.2609068  0.6783576  3.365697  10.175364  0.9914457    0.1304534  0.8088109  0.2609068     3.339607 0.10436270  2.635158
стояти     7.390077 0.2266895  0.5893926  2.924294   8.840890  0.8614200    0.1133447  0.7027374  0.2266895     2.901625 0.09067579  2.289564
творити    7.668948 0.2352438  0.6116339  3.034645   9.174508  0.8939264    0.1176219  0.7292558  0.2352438     3.011121 0.09409752  2.375962
учинити    8.784431 0.2694611  0.7005988  3.476048  10.508982  1.0239521    0.1347305  0.8353293  0.2694611     3.449102 0.10778443  2.721557
          стати_Lform  учати_Aor учати_Fut учати_Lform хотѣти_Aor хотѣти_Lform хотѣти_Pres
бити         7.961933 0.09366980 38.029940   10.303678  2.3417451    13.113772   34.938837
быти         4.108212 0.04833191 19.622754    5.316510  1.2082977     6.766467   18.027802
воевати      1.890505 0.02224123  9.029940    2.446536  0.5560308     3.113772    8.295979
глаголати    4.035500 0.04747648 19.275449    5.222412  1.1869119     6.646707   17.708725
говорити    12.470060 0.14670659 59.562874   16.137725  3.6676647    20.538922   54.721557
дати         2.144996 0.02523524 10.245509    2.775877  0.6308811     3.532934    9.412746
держати      2.399487 0.02822926 11.461078    3.105218  0.7057314     3.952096   10.529512
дѣлати       3.744654 0.04405475 17.886228    4.846022  1.1013687     6.167665   16.432421
жаловати     1.854149 0.02181352  8.856287    2.399487  0.5453379     3.053892    8.136441
жити         8.071001 0.09495295 38.550898   10.444825  2.3738238    13.293413   35.417451
имѣти        2.326775 0.02737382 11.113772    3.011121  0.6843456     3.832335   10.210436
искати       5.271600 0.06201882 25.179641    6.822070  1.5504705     8.682635   23.133020
итти         4.435415 0.05218135 21.185629    5.739949  1.3045338     7.305389   19.463644
писати       3.708298 0.04362703 17.712575    4.798973  1.0906758     6.107784   16.272883
послати      2.290419 0.02694611 10.940120    2.964072  0.6736527     3.772455   10.050898
посылати     2.072284 0.02437981  9.898204    2.681779  0.6094953     3.413174    9.093670
просити      2.726689 0.03207870 13.023952    3.528657  0.8019675     4.491018   11.965355
пѣти         2.290419 0.02694611 10.940120    2.964072  0.6736527     3.772455   10.050898
служити      2.763045 0.03250642 13.197605    3.575706  0.8126604     4.550898   12.124893
сотворити    2.217707 0.02609068 10.592814    2.869974  0.6522669     3.652695    9.731822
стояти       1.926861 0.02266895  9.203593    2.493584  0.5667237     3.173653    8.455518
творити      1.999572 0.02352438  9.550898    2.587682  0.5881095     3.293413    8.774594
учинити      2.290419 0.02694611 10.940120    2.964072  0.6736527     3.772455   10.050898

Команда chisq.test(infinitives) вызывает предупреждение Chi-squared approximation may be incorrect, поскольку некоторые ожидаемые значения меньше 5. Это связано с тем, что тест хи-квадрат следует непрерывному распределению, но на малых выборках статистики не распределены непрерывно.

Baloon plot

С помощью функции balloonplot визуализируем данные в таблице сопряженности.

if (!require("gplots")) install.packages("gplots")
library("gplots")

options(repr.plot.width=15, repr.plot.height=15)


infinitives |> as.matrix() |> as.table() |>
  gplots::balloonplot(main ="", xlab ="", ylab="",
              label = FALSE, show.margins = FALSE, text.size=0.5, colsrt=45, colmar=3, rowmar=3)

Хи-квадрат на меньшей таблице

Оставьте меньшее количество строк и столбцов в таблице (вы можете убирать те или иные столбцы или объединять данные из нескольких столбцов или строк). Результирующая таблица должна быть размером не меньше 3 x 3 и корректна для проведения теста Хи-квадрат. Проведите снова тест Хи-квадрат и запишите ваши выводы.

Вычислите величину эффекта для теста с помощью V Крамера.

Визуализируйте таблицу с помощью мозаичного графика.

Запишите ваши выводы по ассоциации данных в меньшей таблице.

# группируем столбцы и суммируем значения
infinitives_grouped <- infinitives %>%
  rownames_to_column(var = "INF") %>%
  pivot_longer(cols=-INF, names_to = ".value",
               names_pattern = "(.*?)_") %>%
  mutate(быти = replace_na(быти, 0)) %>%
  group_by(INF) %>%
  summarise(across(everything(), sum)) %>%
  column_to_rownames('INF')
infinitives_grouped

# берем строки, где минимальное значение > 5
infinitives_short <- infinitives_grouped[c("бити","говорити","дѣлати","жити"),c("быти","начати","почати","стати","учати","хотѣти")]
infinitives_short

infinitives_short_chisq <- chisq.test(infinitives_short)
infinitives_short_chisq

    Pearson's Chi-squared test

data:  infinitives_short
X-squared = 216.82, df = 15, p-value < 2.2e-16

Уменьшился хи-квадрат и степень свободы, а p-value осталось прежним. Следовательно нулевую гипотезу о независимости вспомогательных глаголов и инфинитивов мы по-прежнему можем отвергнуть.

if (!require("lsr")) install.packages("lsr")
G3;Загрузка требуемого пакета: lsr
g
library(lsr)

# Вычисление величины эффекта для теста с помощью V Крамера.
cramersV(infinitives_short)
[1] 0.2865823
if (!require("vcd")) install.packages("vcd")
G3;Загрузка требуемого пакета: vcd
gG3;Загрузка требуемого пакета: grid
g
library(vcd)

infinitives_short

# Мозаичный график
library('vcd')

vcd::mosaic(
  infinitives_short_chisq$observed,
  set_varnames = list(A = "Инфинитив", B = "Вспомогательный глагол"),
  shade = TRUE,
  gp_labels = gpar(fontsize = 6)
)

Интерактивная тепловая карта

Вернемся к полной таблице сопряженности. Используйте функцию heatmaply для интерактивной визуализации ассоциации между строками и столбцами.

if (!require("heatmaply")) install.packages("heatmaply")
library(heatmaply)
heatmaply(infinitives)

Анализ соответствий

В этом разделе мы предлагаем использовать пакеты factoextra и FactoMineR, но вы можете использовать и другие пакеты R. Постройте модель анализа соответствий и запишите ее в переменную ca.model. Выведите summary модели.

if (!require("factoextra")) install.packages("factoextra")
library(factoextra)

if (!require("FactoMineR")) install.packages("FactoMineR")
library(FactoMineR)
ca.model <- FactoMineR::CA(infinitives, ncp = 5, graph = TRUE)

ca.model
**Results of the Correspondence Analysis (CA)**
The row variable has  23  categories; the column variable has 19 categories
The chi square of independence between the two variables is equal to 4569.931 (p-value =  0 ).
*The results are available in the following objects:

   name              description                   
1  "$eig"            "eigenvalues"                 
2  "$col"            "results for the columns"     
3  "$col$coord"      "coord. for the columns"      
4  "$col$cos2"       "cos2 for the columns"        
5  "$col$contrib"    "contributions of the columns"
6  "$row"            "results for the rows"        
7  "$row$coord"      "coord. for the rows"         
8  "$row$cos2"       "cos2 for the rows"           
9  "$row$contrib"    "contributions of the rows"   
10 "$call"           "summary called parameters"   
11 "$call$marge.col" "weights of the columns"      
12 "$call$marge.row" "weights of the rows"         
print(ca.model)
**Results of the Correspondence Analysis (CA)**
The row variable has  23  categories; the column variable has 19 categories
The chi square of independence between the two variables is equal to 4569.931 (p-value =  0 ).
*The results are available in the following objects:

   name              description                   
1  "$eig"            "eigenvalues"                 
2  "$col"            "results for the columns"     
3  "$col$coord"      "coord. for the columns"      
4  "$col$cos2"       "cos2 for the columns"        
5  "$col$contrib"    "contributions of the columns"
6  "$row"            "results for the rows"        
7  "$row$coord"      "coord. for the rows"         
8  "$row$cos2"       "cos2 for the rows"           
9  "$row$contrib"    "contributions of the rows"   
10 "$call"           "summary called parameters"   
11 "$call$marge.col" "weights of the columns"      
12 "$call$marge.row" "weights of the rows"         

Screeplot

Визуализируйте screeplot.

Запишите ваши выводы, какую долю объясненной дисперсии представляют первые два измерения?

factoextra::fviz_screeplot(ca.model, addlabels = TRUE, ylim = c(0, 50))

Первые два измерения представляют 47% объясненной дисперсии.

Biplot

Постройте базовый биплот.

Постройте плот для данных в столбцах. Цвет должен отражать сontribution каждого конкретного столбца.

Здесь вы можете построить более продвинутый биплот с использованием цвета, прозрачности, repel.

factoextra::fviz_ca_biplot(ca.model, col.col="contrib", col.row = "black", gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"), repel = TRUE, alpha.col=1, alpha.row=0.3)
Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.

для лучшей читабельности, т.к. не получилось изменить прозрачность лэйблов:


factoextra::fviz_ca_col(ca.model, col.col="contrib", col.row = "black", gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"), repel = TRUE, alpha.col=1, alpha.row=0.3)

Выводы

Предложите ваш анализ визуализаций, полученных методом Анализа соответствий.

- вспомогательные глаголы `мочи` и `хотѣти` чаще употребляются с инфинитивами `сотворити`, `учинити`, `итти`, `дати`, `послати`
- вспомогательные глаголы `почати`, `начати`, `учати` и `стати` чаще употребляются с инфинитивами `говорити`, `воевати`, `пѣти`, `глаголати`, `жити`, `просити`, `бити`, `посылати`, `дѣлати`, `творити`

Полезные ссылки:

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIEhXNTogQ29ycmVzcG9uZGVuY2UgYW5hbHlzaXMNCg0KYGBgICAgICAgICAgDQrQoNCw0LHQvtGC0LAg0JvQuC3QnNC40L0g0JLQu9Cw0LTQuNGB0LvQsNCy0YsNCmBgYA0KDQpgYGB7cn0NCmlmICghcmVxdWlyZSgidGlkeXZlcnNlIikpIGluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpDQppZiAoIXJlcXVpcmUoInNraW1yIikpIGluc3RhbGwucGFja2FnZXMoInNraW1yIikNCg0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHNraW1yKQ0KDQppbmZpbml0aXZlcyA8LSByZWFkX3RzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL29sZXNhci8yMDI1ZGF2NGNvbXBsaW5nL3JlZnMvaGVhZHMvbWFpbi9kYXRhL1BlcmlwaHJhc3RpY0Z1dHVyZU1pZFJ1c3NpYW4udHh0IiwgY29sX25hbWVzID0gVFJVRSkgJT4lDQogIGFzLmRhdGEuZnJhbWUoKSAlPiUNCiAgICBzZWxlY3QoIWVuZHNfd2l0aCgiX0ltcCIpKSAlPiUNCiAgICAgIHNlbGVjdCgh0LHRi9GC0LhfTGZvcm0pDQoNCmBgYA0KDQojIyDQn9GA0L7QstC10YDQutCwINC00LDRgtCw0YHQtdGC0LANCg0K0J/QvtC70YzQt9GD0Y/RgdGMINGB0YLQvtC70LHRhtC+0LwgSU5GLCDQtNCw0LnRgtC1INC40LzRjyDQutCw0LbQtNC+0LzRgyDRgNGP0LTRgyDRgtCw0LHQu9C40YbRiyDRgdC+0L/RgNGP0LbQtdC90L3QvtGB0YLQuCAo0YEg0L/QvtC80L7RidGM0Y4g0YTRg9C90LrRhtC40Lggcm93Lm5hbWVzINC40LvQuCBjb2x1bW5fdG9fcm93bmFtZXMpLiDQntGB0YLQsNCy0YzRgtC1INCyINGC0LDQsdC70LjRhtC1INGC0L7Qu9GM0LrQviDRhtC10LvQvtGH0LjRgdC70LXQvdC90YvQtSDQtNCw0L3QvdGL0LUuINCS0YvQstC10LTQuNGC0LUg0L/QtdGA0LLRi9C1IDYg0YHRgtGA0L7QuiDRgtCw0LHQu9C40YbRiy4g0J/RgNC+0LLQtdGA0YzRgtC1LCDRh9GC0L4g0YLQsNCx0LvQuNGG0LAg0L3QtSDRgdC+0LTQtdGA0LbQuNGCIG1pc3NpbmcgZGF0YS4NCg0KYGBge3J9DQpgYGANCg0KYGBge3J9DQojINC/0YDQtdCy0YDQsNGJ0LDQtdC8INGB0YLQvtC70LHQtdGGIElORiDQsiDQuNC90LTQtdC60YENCmluZmluaXRpdmVzIDwtIGNvbHVtbl90b19yb3duYW1lcyhpbmZpbml0aXZlcywgJ0lORicpDQoNCiMg0L/RgNC+0LLQtdGA0Y/QtdC8LCDQvdC10YIg0LvQuCDQv9GA0L7Qv9GD0YnQtdC90L3Ri9GFINC00LDQvdC90YvRhQ0Kc3VtKGlzLm5hKGluZmluaXRpdmVzKSkNCg0KdG9wX24oaW5maW5pdGl2ZXMsIDYpDQpgYGANCg0KIyMgQ2hpc3EgdGVzdA0KDQrQn9GA0LjQvNC10L3QuNGC0LUg0YLQtdGB0YIg0KXQuC3QutCy0LDQtNGA0LDRgiDQn9C40YDRgdC+0L3QsCDQuiDRgtCw0LHQu9C40YbQtSDRgdC+0L/RgNGP0LbQtdC90L3QvtGB0YLQuC4NCg0K0J/QvtGB0YLRgNC+0LnRgtC1INGC0LDQsdC70LjRhtGDINGBINC+0LbQuNC00LDQtdC80YvQvNC4INC30L3QsNGH0LXQvdC40Y/QvNC4INC00LvRjyDRgtCw0LHQu9C40YbRiyDRgdC+0L/RgNGP0LbQtdC90L3QvtGB0YLQuC4NCg0K0J/RgNC10LTQu9C+0LbQuNGC0LUg0LDQvdCw0LvQuNC3INGC0LXRgdGC0LAg0KXQuC3QutCy0LDQtNGA0LDRgi4g0KfRgtC+INC80L7QttC90L4g0YHQutCw0LfQsNGC0Ywg0L7QsSDQsNGB0YHQvtGG0LjQsNGG0LjQuCDQvNC10LbQtNGDINC60L7QvdGB0YLRgNGD0LrRhtC40Y/QvNC4ICjQvtC90Lgg0L7QsdC+0LfQvdCw0YfQtdC90Ysg0LLRgdC/0L7QvNC+0LPQsNGC0LXQu9GM0L3Ri9C80Lgg0LPQu9Cw0LPQvtC70LDQvNC4INCyINGC0L7QuSDQuNC70Lgg0LjQvdC+0Lkg0LLRgNC10LzQtdC90L3QvtC5INGE0L7RgNC80LUpINC4INC40L3RhNC40L3QuNGC0LjQstCw0LzQuD8g0JrQsNC60LjQtSDQv9GA0L7QsdC70LXQvNGLINCy0Ysg0LLQuNC00LjRgtC1INCyINC/0YDQuNC80LXQvdC10L3QuNC4INGC0LXRgdGC0LAg0KXQuC3QutCy0LDQtNGA0LDRgiDQuiDRjdGC0LjQvCDQtNCw0L3QvdGL0Lw/DQoNCmBgYHtyfQ0KIyDQv9GA0LjQvNC10L3Rj9C10Lwg0YLQtdGB0YIg0KXQuC3QutCy0LDQtNGA0LDRgiDQn9C40YDRgdC+0L3QsA0KaW5maW5pdGl2ZXNfY2hpc3EgPC0gY2hpc3EudGVzdChpbmZpbml0aXZlcykNCmluZmluaXRpdmVzX2NoaXNxDQpgYGANCg0KKirQodGC0LDRgtC40YHRgtC40YfQtdGB0LrQsNGPINC40L3RgtC10YDQv9GA0LXRgtCw0YbQuNGPIHAtdmFsdWUqKg0KDQpQLXZhbHVlINC80LXQvdGM0YjQtSAwLjA1LCDQsCDQt9C90LDRh9C40YIsINC/0YDQuCA1JS3QvtC8INGD0YDQvtCy0L3QtSDQt9C90LDRh9C40LzQvtGB0YLQuCDQvNGLINC40LzQtdC10Lwg0L7RgdC90L7QstCw0L3QuNGPINC+0YLQstC10YDQs9C90YPRgtGMINC90YPQu9C10LLRg9GOINCz0LjQv9C+0YLQtdC30YMg0L4g0L3QtdC30LDQstC40YHQuNC80L7RgdGC0Lgg0YPQv9C+0YLRgNC10LHQu9C10L3QuNGPINCy0YHQv9C+0LzQvtCz0LDRgtC10LvRjNC90YvRhSDQs9C70LDQs9C+0LvQvtCyINC4INC40L3RhNC40L3QuNGC0LjQstC+0LIuDQoNCioq0KHQvtC00LXRgNC20LDRgtC10LvRjNC90LDRjyDQuNC90YLQtdGA0L/RgNC10YLQsNGG0LjRjyoqDQoNCtCS0YvQsdC+0YAg0L3QsNGH0LjQvdCw0YLQtdC70YzQvdC+0LPQviDQs9C70LDQs9C+0LvQsCDQt9Cw0LLQuNGB0LjRgiDQvtGCINGB0LvQtdC00YPRjtGJ0LXQs9C+INC30LAg0L3QuNC8INC40L3RhNC40L3QuNGC0LjQstCwLg0KDQpgYGB7cn0NCiMg0L/RgNC+0LLQtdGA0Y/QtdC8INC/0YDQvtC/0YPRidC10L3QvdGL0LUg0LfQvdCw0YfQtdC90LjRjw0Kc3VtKGlzLm5hKGluZmluaXRpdmVzKSkNCg0KIyDQotCw0LHQu9C40YbQsCDRgSDQvtC20LjQtNCw0LXQvNGL0LzQuCDQt9C90LDRh9C10L3QuNGP0LzQuA0KaW5maW5pdGl2ZXNfY2hpc3EkZXhwZWN0ZWQNCmBgYA0KDQrQmtC+0LzQsNC90LTQsCBgY2hpc3EudGVzdChpbmZpbml0aXZlcylgINCy0YvQt9GL0LLQsNC10YIg0L/RgNC10LTRg9C/0YDQtdC20LTQtdC90LjQtSBgQ2hpLXNxdWFyZWQgYXBwcm94aW1hdGlvbiBtYXkgYmUgaW5jb3JyZWN0YCwg0L/QvtGB0LrQvtC70YzQutGDINC90LXQutC+0YLQvtGA0YvQtSDQvtC20LjQtNCw0LXQvNGL0LUg0LfQvdCw0YfQtdC90LjRjyDQvNC10L3RjNGI0LUgNS4g0K3RgtC+INGB0LLRj9C30LDQvdC+INGBINGC0LXQvCwg0YfRgtC+INGC0LXRgdGCINGF0Lgt0LrQstCw0LTRgNCw0YIg0YHQu9C10LTRg9C10YIg0L3QtdC/0YDQtdGA0YvQstC90L7QvNGDINGA0LDRgdC/0YDQtdC00LXQu9C10L3QuNGOLCDQvdC+INC90LAg0LzQsNC70YvRhSDQstGL0LHQvtGA0LrQsNGFINGB0YLQsNGC0LjRgdGC0LjQutC4INC90LUg0YDQsNGB0L/RgNC10LTQtdC70LXQvdGLINC90LXQv9GA0LXRgNGL0LLQvdC+Lg0KDQojIyBCYWxvb24gcGxvdA0KDQrQoSDQv9C+0LzQvtGJ0YzRjiDRhNGD0L3QutGG0LjQuCBgYmFsbG9vbnBsb3RgINCy0LjQt9GD0LDQu9C40LfQuNGA0YPQtdC8INC00LDQvdC90YvQtSDQsiDRgtCw0LHQu9C40YbQtSDRgdC+0L/RgNGP0LbQtdC90L3QvtGB0YLQuC4NCg0KYGBge3J9DQppZiAoIXJlcXVpcmUoImdwbG90cyIpKSBpbnN0YWxsLnBhY2thZ2VzKCJncGxvdHMiKQ0KbGlicmFyeSgiZ3Bsb3RzIikNCg0Kb3B0aW9ucyhyZXByLnBsb3Qud2lkdGg9MTUsIHJlcHIucGxvdC5oZWlnaHQ9MTUpDQoNCg0KaW5maW5pdGl2ZXMgfD4gYXMubWF0cml4KCkgfD4gYXMudGFibGUoKSB8Pg0KICBncGxvdHM6OmJhbGxvb25wbG90KG1haW4gPSIiLCB4bGFiID0iIiwgeWxhYj0iIiwNCiAgICAgICAgICAgICAgbGFiZWwgPSBGQUxTRSwgc2hvdy5tYXJnaW5zID0gRkFMU0UsIHRleHQuc2l6ZT0wLjUsIGNvbHNydD00NSwgY29sbWFyPTMsIHJvd21hcj0zKQ0KYGBgDQoNCiMjINCl0Lgt0LrQstCw0LTRgNCw0YIg0L3QsCDQvNC10L3RjNGI0LXQuSDRgtCw0LHQu9C40YbQtQ0KDQrQntGB0YLQsNCy0YzRgtC1INC80LXQvdGM0YjQtdC1INC60L7Qu9C40YfQtdGB0YLQstC+INGB0YLRgNC+0Log0Lgg0YHRgtC+0LvQsdGG0L7QsiDQsiDRgtCw0LHQu9C40YbQtSAo0LLRiyDQvNC+0LbQtdGC0LUg0YPQsdC40YDQsNGC0Ywg0YLQtSDQuNC70Lgg0LjQvdGL0LUg0YHRgtC+0LvQsdGG0Ysg0LjQu9C4INC+0LHRitC10LTQuNC90Y/RgtGMINC00LDQvdC90YvQtSDQuNC3INC90LXRgdC60L7Qu9GM0LrQuNGFINGB0YLQvtC70LHRhtC+0LIg0LjQu9C4INGB0YLRgNC+0LopLiDQoNC10LfRg9C70YzRgtC40YDRg9GO0YnQsNGPINGC0LDQsdC70LjRhtCwINC00L7Qu9C20L3QsCDQsdGL0YLRjCDRgNCw0LfQvNC10YDQvtC8INC90LUg0LzQtdC90YzRiNC1IDMgeCAzINC4INC60L7RgNGA0LXQutGC0L3QsCDQtNC70Y8g0L/RgNC+0LLQtdC00LXQvdC40Y8g0YLQtdGB0YLQsCDQpdC4LdC60LLQsNC00YDQsNGCLiDQn9GA0L7QstC10LTQuNGC0LUg0YHQvdC+0LLQsCDRgtC10YHRgiDQpdC4LdC60LLQsNC00YDQsNGCINC4INC30LDQv9C40YjQuNGC0LUg0LLQsNGI0Lgg0LLRi9Cy0L7QtNGLLg0KDQrQktGL0YfQuNGB0LvQuNGC0LUg0LLQtdC70LjRh9C40L3RgyDRjdGE0YTQtdC60YLQsCDQtNC70Y8g0YLQtdGB0YLQsCDRgSDQv9C+0LzQvtGJ0YzRjiBWINCa0YDQsNC80LXRgNCwLg0KDQrQktC40LfRg9Cw0LvQuNC30LjRgNGD0LnRgtC1INGC0LDQsdC70LjRhtGDINGBINC/0L7QvNC+0YnRjNGOINC80L7Qt9Cw0LjRh9C90L7Qs9C+INCz0YDQsNGE0LjQutCwLg0KDQrQl9Cw0L/QuNGI0LjRgtC1INCy0LDRiNC4INCy0YvQstC+0LTRiyDQv9C+INCw0YHRgdC+0YbQuNCw0YbQuNC4INC00LDQvdC90YvRhSDQsiDQvNC10L3RjNGI0LXQuSDRgtCw0LHQu9C40YbQtS4NCg0KYGBge3J9DQojINCz0YDRg9C/0L/QuNGA0YPQtdC8INGB0YLQvtC70LHRhtGLINC4INGB0YPQvNC80LjRgNGD0LXQvCDQt9C90LDRh9C10L3QuNGPDQppbmZpbml0aXZlc19ncm91cGVkIDwtIGluZmluaXRpdmVzICU+JQ0KICByb3duYW1lc190b19jb2x1bW4odmFyID0gIklORiIpICU+JQ0KICBwaXZvdF9sb25nZXIoY29scz0tSU5GLCBuYW1lc190byA9ICIudmFsdWUiLA0KICAgICAgICAgICAgICAgbmFtZXNfcGF0dGVybiA9ICIoLio/KV8iKSAlPiUNCiAgbXV0YXRlKNCx0YvRgtC4ID0gcmVwbGFjZV9uYSjQsdGL0YLQuCwgMCkpICU+JQ0KICBncm91cF9ieShJTkYpICU+JQ0KICBzdW1tYXJpc2UoYWNyb3NzKGV2ZXJ5dGhpbmcoKSwgc3VtKSkgJT4lDQogIGNvbHVtbl90b19yb3duYW1lcygnSU5GJykNCmluZmluaXRpdmVzX2dyb3VwZWQNCg0KIyDQsdC10YDQtdC8INGB0YLRgNC+0LrQuCwg0LPQtNC1INC80LjQvdC40LzQsNC70YzQvdC+0LUg0LfQvdCw0YfQtdC90LjQtSA+IDUNCmluZmluaXRpdmVzX3Nob3J0IDwtIGluZmluaXRpdmVzX2dyb3VwZWRbYygi0LHQuNGC0LgiLCLQs9C+0LLQvtGA0LjRgtC4Iiwi0LTRo9C70LDRgtC4Iiwi0LbQuNGC0LgiKSxjKCLQsdGL0YLQuCIsItC90LDRh9Cw0YLQuCIsItC/0L7Rh9Cw0YLQuCIsItGB0YLQsNGC0LgiLCLRg9GH0LDRgtC4Iiwi0YXQvtGC0aPRgtC4IildDQppbmZpbml0aXZlc19zaG9ydA0KDQppbmZpbml0aXZlc19zaG9ydF9jaGlzcSA8LSBjaGlzcS50ZXN0KGluZmluaXRpdmVzX3Nob3J0KQ0KaW5maW5pdGl2ZXNfc2hvcnRfY2hpc3ENCg0KYGBgDQoNCtCj0LzQtdC90YzRiNC40LvRgdGPINGF0Lgt0LrQstCw0LTRgNCw0YIg0Lgg0YHRgtC10L/QtdC90Ywg0YHQstC+0LHQvtC00YssINCwIHAtdmFsdWUg0L7RgdGC0LDQu9C+0YHRjCDQv9GA0LXQttC90LjQvC4g0KHQu9C10LTQvtCy0LDRgtC10LvRjNC90L4g0L3Rg9C70LXQstGD0Y4g0LPQuNC/0L7RgtC10LfRgyDQviDQvdC10LfQsNCy0LjRgdC40LzQvtGB0YLQuCDQstGB0L/QvtC80L7Qs9Cw0YLQtdC70YzQvdGL0YUg0LPQu9Cw0LPQvtC70L7QsiDQuCDQuNC90YTQuNC90LjRgtC40LLQvtCyINC80Ysg0L/Qvi3Qv9GA0LXQttC90LXQvNGDINC80L7QttC10Lwg0L7RgtCy0LXRgNCz0L3Rg9GC0YwuDQoNCmBgYHtyfQ0KaWYgKCFyZXF1aXJlKCJsc3IiKSkgaW5zdGFsbC5wYWNrYWdlcygibHNyIikNCmxpYnJhcnkobHNyKQ0KDQojINCS0YvRh9C40YHQu9C10L3QuNC1INCy0LXQu9C40YfQuNC90Ysg0Y3RhNGE0LXQutGC0LAg0LTQu9GPINGC0LXRgdGC0LAg0YEg0L/QvtC80L7RidGM0Y4gViDQmtGA0LDQvNC10YDQsC4NCmNyYW1lcnNWKGluZmluaXRpdmVzX3Nob3J0KQ0KDQppZiAoIXJlcXVpcmUoInZjZCIpKSBpbnN0YWxsLnBhY2thZ2VzKCJ2Y2QiKQ0KbGlicmFyeSh2Y2QpDQoNCmluZmluaXRpdmVzX3Nob3J0DQoNCiMg0JzQvtC30LDQuNGH0L3Ri9C5INCz0YDQsNGE0LjQug0KbGlicmFyeSgndmNkJykNCg0KdmNkOjptb3NhaWMoDQogIGluZmluaXRpdmVzX3Nob3J0X2NoaXNxJG9ic2VydmVkLA0KICBzZXRfdmFybmFtZXMgPSBsaXN0KEEgPSAi0JjQvdGE0LjQvdC40YLQuNCyIiwgQiA9ICLQktGB0L/QvtC80L7Qs9Cw0YLQtdC70YzQvdGL0Lkg0LPQu9Cw0LPQvtC7IiksDQogIHNoYWRlID0gVFJVRSwNCiAgZ3BfbGFiZWxzID0gZ3Bhcihmb250c2l6ZSA9IDYpDQopDQpgYGANCg0KIyMg0JjQvdGC0LXRgNCw0LrRgtC40LLQvdCw0Y8g0YLQtdC/0LvQvtCy0LDRjyDQutCw0YDRgtCwDQoNCtCS0LXRgNC90LXQvNGB0Y8g0Log0L/QvtC70L3QvtC5INGC0LDQsdC70LjRhtC1INGB0L7Qv9GA0Y/QttC10L3QvdC+0YHRgtC4LiDQmNGB0L/QvtC70YzQt9GD0LnRgtC1INGE0YPQvdC60YbQuNGOIGhlYXRtYXBseSDQtNC70Y8g0LjQvdGC0LXRgNCw0LrRgtC40LLQvdC+0Lkg0LLQuNC30YPQsNC70LjQt9Cw0YbQuNC4INCw0YHRgdC+0YbQuNCw0YbQuNC4INC80LXQttC00YMg0YHRgtGA0L7QutCw0LzQuCDQuCDRgdGC0L7Qu9Cx0YbQsNC80LguDQoNCmBgYHtyfQ0KaWYgKCFyZXF1aXJlKCJoZWF0bWFwbHkiKSkgaW5zdGFsbC5wYWNrYWdlcygiaGVhdG1hcGx5IikNCmxpYnJhcnkoaGVhdG1hcGx5KQ0KYGBgDQoNCmBgYHtyfQ0KaGVhdG1hcGx5KGluZmluaXRpdmVzKQ0KYGBgDQoNCiMjINCQ0L3QsNC70LjQtyDRgdC+0L7RgtCy0LXRgtGB0YLQstC40LkNCg0K0JIg0Y3RgtC+0Lwg0YDQsNC30LTQtdC70LUg0LzRiyDQv9GA0LXQtNC70LDQs9Cw0LXQvCDQuNGB0L/QvtC70YzQt9C+0LLQsNGC0Ywg0L/QsNC60LXRgtGLIGZhY3RvZXh0cmEg0LggRmFjdG9NaW5lUiwg0L3QviDQstGLINC80L7QttC10YLQtSDQuNGB0L/QvtC70YzQt9C+0LLQsNGC0Ywg0Lgg0LTRgNGD0LPQuNC1INC/0LDQutC10YLRiyBSLiDQn9C+0YHRgtGA0L7QudGC0LUg0LzQvtC00LXQu9GMINCw0L3QsNC70LjQt9CwINGB0L7QvtGC0LLQtdGC0YHRgtCy0LjQuSDQuCDQt9Cw0L/QuNGI0LjRgtC1INC10LUg0LIg0L/QtdGA0LXQvNC10L3QvdGD0Y4gY2EubW9kZWwuINCS0YvQstC10LTQuNGC0LUgc3VtbWFyeSDQvNC+0LTQtdC70LguDQoNCmBgYHtyfQ0KaWYgKCFyZXF1aXJlKCJmYWN0b2V4dHJhIikpIGluc3RhbGwucGFja2FnZXMoImZhY3RvZXh0cmEiKQ0KbGlicmFyeShmYWN0b2V4dHJhKQ0KDQppZiAoIXJlcXVpcmUoIkZhY3RvTWluZVIiKSkgaW5zdGFsbC5wYWNrYWdlcygiRmFjdG9NaW5lUiIpDQpsaWJyYXJ5KEZhY3RvTWluZVIpDQpgYGANCg0KYGBge3J9DQpjYS5tb2RlbCA8LSBGYWN0b01pbmVSOjpDQShpbmZpbml0aXZlcywgbmNwID0gNSwgZ3JhcGggPSBUUlVFKQ0KY2EubW9kZWwNCmBgYA0KDQpgYGB7cn0NCnByaW50KGNhLm1vZGVsKQ0KYGBgDQoNCiMjIFNjcmVlcGxvdA0KDQrQktC40LfRg9Cw0LvQuNC30LjRgNGD0LnRgtC1IHNjcmVlcGxvdC4NCg0K0JfQsNC/0LjRiNC40YLQtSDQstCw0YjQuCDQstGL0LLQvtC00YssINC60LDQutGD0Y4g0LTQvtC70Y4g0L7QsdGK0Y/RgdC90LXQvdC90L7QuSDQtNC40YHQv9C10YDRgdC40Lgg0L/RgNC10LTRgdGC0LDQstC70Y/RjtGCINC/0LXRgNCy0YvQtSDQtNCy0LAg0LjQt9C80LXRgNC10L3QuNGPPw0KDQpgYGB7cn0NCmZhY3RvZXh0cmE6OmZ2aXpfc2NyZWVwbG90KGNhLm1vZGVsLCBhZGRsYWJlbHMgPSBUUlVFLCB5bGltID0gYygwLCA1MCkpDQpgYGANCg0K0J/QtdGA0LLRi9C1INC00LLQsCDQuNC30LzQtdGA0LXQvdC40Y8g0L/RgNC10LTRgdGC0LDQstC70Y/RjtGCIDQ3JSDQvtCx0YrRj9GB0L3QtdC90L3QvtC5INC00LjRgdC/0LXRgNGB0LjQuC4NCg0KIyMgQmlwbG90DQoNCtCf0L7RgdGC0YDQvtC50YLQtSDQsdCw0LfQvtCy0YvQuSDQsdC40L/Qu9C+0YIuDQoNCtCf0L7RgdGC0YDQvtC50YLQtSDQv9C70L7RgiDQtNC70Y8g0LTQsNC90L3Ri9GFINCyINGB0YLQvtC70LHRhtCw0YUuINCm0LLQtdGCINC00L7Qu9C20LXQvSDQvtGC0YDQsNC20LDRgtGMINGBb250cmlidXRpb24g0LrQsNC20LTQvtCz0L4g0LrQvtC90LrRgNC10YLQvdC+0LPQviDRgdGC0L7Qu9Cx0YbQsC4NCg0K0JfQtNC10YHRjCDQstGLINC80L7QttC10YLQtSDQv9C+0YHRgtGA0L7QuNGC0Ywg0LHQvtC70LXQtSDQv9GA0L7QtNCy0LjQvdGD0YLRi9C5INCx0LjQv9C70L7RgiDRgSDQuNGB0L/QvtC70YzQt9C+0LLQsNC90LjQtdC8INGG0LLQtdGC0LAsINC/0YDQvtC30YDQsNGH0L3QvtGB0YLQuCwgcmVwZWwuDQoNCmBgYHtyfQ0KZmFjdG9leHRyYTo6ZnZpel9jYV9iaXBsb3QoY2EubW9kZWwsIGNvbC5jb2w9ImNvbnRyaWIiLCBjb2wucm93ID0gImJsYWNrIiwgZ3JhZGllbnQuY29scyA9IGMoIiMwMEFGQkIiLCAiI0U3QjgwMCIsICIjRkM0RTA3IiksIHJlcGVsID0gVFJVRSwgYWxwaGEuY29sPTEsIGFscGhhLnJvdz0wLjMpDQoNCmBgYA0KDQrQtNC70Y8g0LvRg9GH0YjQtdC5INGH0LjRgtCw0LHQtdC70YzQvdC+0YHRgtC4LCDRgi7Qui4g0L3QtSDQv9C+0LvRg9GH0LjQu9C+0YHRjCDQuNC30LzQtdC90LjRgtGMINC/0YDQvtC30YDQsNGH0L3QvtGB0YLRjCDQu9GN0LnQsdC70L7QsjoNCg0KYGBge3J9DQoNCmZhY3RvZXh0cmE6OmZ2aXpfY2FfY29sKGNhLm1vZGVsLCBjb2wuY29sPSJjb250cmliIiwgY29sLnJvdyA9ICJibGFjayIsIGdyYWRpZW50LmNvbHMgPSBjKCIjMDBBRkJCIiwgIiNFN0I4MDAiLCAiI0ZDNEUwNyIpLCByZXBlbCA9IFRSVUUsIGFscGhhLmNvbD0xLCBhbHBoYS5yb3c9MC4zKQ0KDQpgYGANCg0KIyMg0JLRi9Cy0L7QtNGLDQoNCtCf0YDQtdC00LvQvtC20LjRgtC1INCy0LDRiCDQsNC90LDQu9C40Lcg0LLQuNC30YPQsNC70LjQt9Cw0YbQuNC5LCDQv9C+0LvRg9GH0LXQvdC90YvRhSDQvNC10YLQvtC00L7QvCDQkNC90LDQu9C40LfQsCDRgdC+0L7RgtCy0LXRgtGB0YLQstC40LkuDQoNCmBgYCAgICAgICAgIA0KLSDQstGB0L/QvtC80L7Qs9Cw0YLQtdC70YzQvdGL0LUg0LPQu9Cw0LPQvtC70YsgYNC80L7Rh9C4YCDQuCBg0YXQvtGC0aPRgtC4YCDRh9Cw0YnQtSDRg9C/0L7RgtGA0LXQsdC70Y/RjtGC0YHRjyDRgSDQuNC90YTQuNC90LjRgtC40LLQsNC80LggYNGB0L7RgtCy0L7RgNC40YLQuGAsIGDRg9GH0LjQvdC40YLQuGAsIGDQuNGC0YLQuGAsIGDQtNCw0YLQuGAsIGDQv9C+0YHQu9Cw0YLQuGANCi0g0LLRgdC/0L7QvNC+0LPQsNGC0LXQu9GM0L3Ri9C1INCz0LvQsNCz0L7Qu9GLIGDQv9C+0YfQsNGC0LhgLCBg0L3QsNGH0LDRgtC4YCwgYNGD0YfQsNGC0LhgINC4IGDRgdGC0LDRgtC4YCDRh9Cw0YnQtSDRg9C/0L7RgtGA0LXQsdC70Y/RjtGC0YHRjyDRgSDQuNC90YTQuNC90LjRgtC40LLQsNC80LggYNCz0L7QstC+0YDQuNGC0LhgLCBg0LLQvtC10LLQsNGC0LhgLCBg0L/Ro9GC0LhgLCBg0LPQu9Cw0LPQvtC70LDRgtC4YCwgYNC20LjRgtC4YCwgYNC/0YDQvtGB0LjRgtC4YCwgYNCx0LjRgtC4YCwgYNC/0L7RgdGL0LvQsNGC0LhgLCBg0LTRo9C70LDRgtC4YCwgYNGC0LLQvtGA0LjRgtC4YA0KYGBgDQoNCtCf0L7Qu9C10LfQvdGL0LUg0YHRgdGL0LvQutC4Og0KDQotICAgPGh0dHBzOi8vc3RhdHNhbmRyLmNvbS9ibG9nL2NoaS1zcXVhcmUtdGVzdC1vZi1pbmRlcGVuZGVuY2UtaW4tci8+DQoNCi0gICA8aHR0cHM6Ly93d3cuc3RoZGEuY29tL2VuZ2xpc2gvYXJ0aWNsZXMvMzEtcHJpbmNpcGFsLWNvbXBvbmVudC1tZXRob2RzLWluLXItcHJhY3RpY2FsLWd1aWRlLzExMy1jYS1jb3JyZXNwb25kZW5jZS1hbmFseXNpcy1pbi1yLWVzc2VudGlhbHMvPg0K